/*

Supported target OS list

+------------+------------+-----------------+
|   OS Name  | OS Version | OS Build Number |
+------------+------------+-----------------+
| Windows 10 |    2004    |      19041      |
+------------+------------+-----------------+
| Windows 10 |    1909    |      18363      |
+------------+------------+-----------------+
| Windows 10 |    1903    |      18362      |
+------------+------------+-----------------+
| Windows 10 |    1809    |      17763      |
+------------+------------+-----------------+

*/

std::vector <std::string> installedUpdates;

//https://stackoverflow.com/questions/6284524/bstr-to-stdstring-stdwstring-and-vice-versa
std::string convertWCSToMBS(const wchar_t* pstr, long wslen) {
    int len = ::WideCharToMultiByte(CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL);

    std::string dblstr(len, '\0');
    len = WideCharToMultiByte(CP_ACP, 0 /* no flags */,
        pstr, wslen /* not necessary NULL-terminated */,
        &dblstr[0], len,
        NULL, NULL /* no default char */);

    return dblstr;
}

std::string convertBSTRToMBS(BSTR bstr) {
    int wslen = SysStringLen(bstr);
    return convertWCSToMBS((wchar_t*)bstr, wslen);
}

std::string kbParser(IStringCollection* pNodeGroups) {
    std::string kbNumber;

    HRESULT hr = S_OK;
    BSTR bstr = NULL;
    long count = 0;

    hr = pNodeGroups->get_Count(&count);
    if (FAILED(hr)) {
        wprintf(L"pNodeGroups->get_Count failed with 0x%x.\n", hr);
    }

    for (long i = 0; i < count; i++) {
        hr = pNodeGroups->get_Item(i, &bstr);
        if (FAILED(hr)) {
            wprintf(L"pNodeGroups->get_Item failed with 0x%x.\n", hr);
            break;
        }

        //wprintf(L"\t%s\n", bstr);
        kbNumber = convertBSTRToMBS(bstr);
        SysFreeString(bstr);
        bstr = NULL;
    }

    return kbNumber;
}

//There is following control because of under Network&Local Service Account, CoCreateInstance not working.
bool isUpdatesCheckable() {
    bool getUpdateResult = FALSE;

    if ((detectProcessUser() != "NT AUTHORITY\\NETWORK SERVICE") and (detectProcessUser() != "NT AUTHORITY\\LOCAL SERVICE")) {
        getUpdateResult = TRUE;
    }
    else {
        getUpdateResult = FALSE;
    }
    return getUpdateResult;
}

#include <wuerror.h>

//https://github.com/cavaliercoder/sysinv/blob/985460bd958fbc38972342fba9f687dd1c0827c7/hotfixes.cpp
bool getInstalledUpdates() {

    bool installedUpdatesResult = FALSE;

    HRESULT hr;
    hr = CoInitialize(NULL);
    IUpdateSession* iUpdate;
    IUpdateSearcher* searcher;
    ISearchResult* results;
    BSTR criteria = SysAllocString(L"IsInstalled=1 or IsHidden=1 or IsPresent=1"); // or IsHidden=1 or IsPresent=1

    hr = CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&iUpdate);
   
    hr = iUpdate->CreateUpdateSearcher(&searcher);
    
    searcher->put_ServerSelection(ssWindowsUpdate);
    searcher->put_IncludePotentiallySupersededUpdates(VARIANT_TRUE);
    searcher->put_Online(VARIANT_FALSE);

    std::cout << "\n[*] Getting installed updates...\n";

    hr = searcher->Search(criteria, &results);
    SysFreeString(criteria);

    if (SUCCEEDED(hr)) {

        IUpdateCollection* updateList;
        LONG updateSize;

        results->get_Updates(&updateList);
        updateList->get_Count(&updateSize);


        for (LONG i = 0; i < updateSize; i++) {
            IUpdate* updateItem;
            BSTR updateName;
            IStringCollection* kbCollection;

            updateList->get_Item(i, &updateItem);
            updateItem->get_Title(&updateName);
            updateItem->get_KBArticleIDs(&kbCollection);

            installedUpdates.push_back(kbParser(kbCollection));
        }

        std::cout << "\n\t" << installedUpdates.size() << " installed update(s) found!";

        std::cout << " KB numbers are: ";
        for (std::string kbList : installedUpdates) {
            std::cout << kbList << " ";
        }

        std::cout << "\n\n[*] Checking for privileges escalation exploits...\n";

        installedUpdatesResult = TRUE;
    }
    else {
        _com_error err(hr);
        LPCWSTR errMsg = err.ErrorMessage();

        std::cout << "\n[!] Cannot checking updates beacuse Windows Update Agent API not working properly... ";
        std::wcout << "Error Details: " << errMsg << "\n";

        installedUpdatesResult = FALSE;
    }

    CoUninitialize();

    return installedUpdatesResult;

}
